package com.rt.easyjava.base.utils;

import com.alibaba.fastjson.util.IOUtils;
import com.github.tobato.fastdfs.domain.fdfs.MetaData;
import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import com.google.common.base.Strings;
import com.google.common.collect.Maps;
import com.rt.easyjava.base.biz1.bean.TmptBaseFileBiz;
import com.rt.easyjava.common.base.Result;
import com.rt.easyjava.common.base.UserDetailsDto;
import com.rt.easyjava.common.utils.others.DateTool;
import com.rt.easyjava.common.utils.random.RandomUtils;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.*;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * fastdfs连接工具类
 *
 * @author zhoup
 * @create 2020-11-24
 **/
@Component
@Slf4j
public class DfsService {

    //可压缩的图片文件格式
    private static final String compressedAbleFiles = "jpg,jpeg,gif,png,tiff";
    //压缩过程中的临时文件存储目录
    private static final String tmpPath = "/tmp/";

    @Value("${fdfs.uri}")
    private String httpUri;
    @Resource
    private FastFileStorageClient storageClient;

    /**
     * 基于流的文件上传；
     * 将流转换为MockMultipartFile=》转换为本地文件；
     *
     * @param inputStream
     * @param biz
     * @return
     * @throws Exception
     */
    public Map<String, Object> uploadFile(InputStream inputStream, TmptBaseFileBiz biz, String suffix) throws Exception {
        Map<String, Object> res = Maps.newHashMap();
        String[] uploadRes;
        //生成随机文件名；避免重复；
        if (!suffix.contains(".")) {
            suffix = ".".concat(suffix);
        }
        String tmpFileName = DateTool.getCurrentDateStr1("yyyyMMddHHmmssSSS") + "-" + RandomUtils.getRandomSimple(10000, 99999) + suffix;
        //将流还原成文件；
        File localFile = inputStream2File(inputStream, new File(tmpFileName));
        File compressedFile = this.compressFile(localFile);
        return this.uploadFile(compressedFile, biz);
    }

    /**
     * 基于流的文件上传；
     * 将流转换为MockMultipartFile=》转换为本地文件；
     *
     * @param file
     * @param biz
     * @return
     * @throws Exception
     */
    public Map<String, Object> uploadFile(MultipartFile file, TmptBaseFileBiz biz) throws Exception {
        Map<String, Object> res = Maps.newHashMap();
        String[] uploadRes;
        //生成随机文件名；避免重复；
        String fileName = file.getOriginalFilename();
        String suffix = fileName.substring(fileName.lastIndexOf("."));
        String tmpFileName = DateTool.getCurrentDateStr1("yyyyMMddHHmmssSSS") + "-" + RandomUtils.getRandomSimple(10000, 99999) + suffix;
        File compressedFile = this.compressFile(file, tmpFileName);
        return this.uploadFile(compressedFile, biz);
    }


    public Map<String, Object> uploadFile(File file, TmptBaseFileBiz biz) throws Exception {
        String[] uploadRes = new String[2];
        File compressedFile = this.compressFile(file);
        String suffix = file.getName().substring(file.getName().lastIndexOf(".")+1);
        Set<MetaData> metaData = createMetaData(file.getName(), suffix, String.valueOf(file.length()));
        long fileSize = compressedFile.length();
        StorePath path = storageClient.uploadFile( new FileInputStream(compressedFile), fileSize, suffix, metaData);
        if (compressedFile.exists()) {
            boolean deleteResult = compressedFile.delete();
            log.info("本地文件删除结果:{}", deleteResult);
        }
        uploadRes[0] = path.getGroup();
        uploadRes[1] = path.getFullPath();
        //保存上传日志；
        biz.setUrlGroup(path.getGroup().concat("/").concat(path.getFullPath()));
        biz.setSize(fileSize);
        biz.setUrl(httpUri.concat(path.getGroup().concat("/").concat(path.getFullPath())));
        createFileLog(biz);
        return buildResultMap(uploadRes);
    }

    private File inputStream2File(InputStream inputStream, File file) {
        try {
            OutputStream os = new FileOutputStream(file);
            int bytesRead = 0;
            byte[] buffer = new byte[8192];
            while ((bytesRead = inputStream.read(buffer, 0, 8192)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            IOUtils.close(os);
            IOUtils.close(inputStream);
            return file;
        } catch (IOException ioException) {
            log.error("【文件上传】-流转换为文件时发生异常，错误信息：{}", ioException);
        }
        return null;
    }




    public boolean deleteFile(String group, String tmpPath) {
        try {
            group = Strings.isNullOrEmpty(group) ? "group1" : group;
            storageClient.deleteFile(group, tmpPath);
            return true;
        } catch (Exception e) {
            log.error("删除文件发生错误，错误信息：{}", e.getMessage());
            return false;
        }
    }

    public static void main(String[] args) {
        String name = "a.b.c.jpg";
        System.out.println(name.substring(name.lastIndexOf(".")));

    }

    private Map<String, Object> buildResultMap(String[] uploadRes) {
        Map<String, Object> resMap = Maps.newHashMap();
        String fileId = uploadRes[1];
        String group = uploadRes[0];
        if (!Strings.isNullOrEmpty(fileId)) {
            resMap.put("code", "0000");
            resMap.put("group", fileId.substring(0, 6));
            resMap.put("msg", fileId.substring(7, fileId.length()));
            resMap.put("fileId", fileId);
        } else {
            resMap.put("code", "0003");
            resMap.put("msg", "error:上传失败!");
        }
        return resMap;
    }

    /**
     * 将前端传过来的文件流转换为本地文件；
     * 如果是图片则进行压缩并返回压缩之后的本地文件，否则直接返回本地文件。
     *
     * @param file      前端传输过来的文件流
     * @param wholeName 完整的文件名；
     * @return 本地文件；
     * @throws IOException
     */
    private File compressFile(MultipartFile file, String wholeName) throws IOException {
        if (Strings.isNullOrEmpty(wholeName)) {
            wholeName = file.getOriginalFilename();
        }
        assert wholeName != null;
        String filePath = tmpPath.concat(wholeName);
        File localFile = new File(filePath);
        FileOutputStream fileOutputStream = null;
        fileOutputStream = new FileOutputStream(localFile);
        byte[] mutilPartFileBytes = file.getBytes();
        for (byte mutilPartFileByte : mutilPartFileBytes) {
            fileOutputStream.write(mutilPartFileByte);
        }
        return compressFile(localFile);
    }

    /**
     * 判断文件是否属于可压缩的类型，如果可压缩，则返回压缩之后的本地文件，否则返回原文件；
     *
     * @param file
     * @return
     * @throws IOException
     */
    private File compressFile(File file) throws IOException {
        String fileName = file.getName();
        String path = tmpPath.concat(fileName);
        String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
        if (!Strings.isNullOrEmpty(suffix) && compressedAbleFiles.contains(suffix)) {
            File localFile = new File(path);
            Thumbnails.of(file).scale(1.0f).outputQuality(0.5f).outputFormat("jpg").allowOverwrite(true).toFile(path);
            return localFile;
        }
        return file;
    }

    // db 记录
    private Result createFileLog(TmptBaseFileBiz tmptBaseFile) {
        String urlFull = httpUri + "/" + tmptBaseFile.getUrlGroup();;
        tmptBaseFile.setUrl(urlFull);
        UserDetailsDto userDetailsDto = UserDetailsDto.getUserDetailsDto(tmptBaseFile.getUserDetailsJson());
        tmptBaseFile.setCreatedate(DateTool.getCurTimestamp());
        if (userDetailsDto!=null) {
            tmptBaseFile.setCreateman(userDetailsDto.getUserName());
            tmptBaseFile.setCreatemanId(Long.parseLong(userDetailsDto.getUserId()));
            tmptBaseFile.setCompanyId(Long.parseLong(userDetailsDto.getCompanyId()));
        }
        tmptBaseFile.getTmptBaseFileBizService().save(tmptBaseFile);
        return Result.builder(tmptBaseFile.getId());
    }


    private Set<MetaData> createMetaData(String fileName, String extName,String fileLength) {
        Set<MetaData> metaDataSet = new HashSet<>();
        metaDataSet.add(new MetaData("fileName", fileName));
        metaDataSet.add(new MetaData("fileExtName", extName));
        metaDataSet.add(new MetaData("fileLength", fileLength));
        return metaDataSet;
    }

}
